package com.zareal.ymcc.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.zareal.ymcc.constants.CourseOrderContants;
import com.zareal.ymcc.domain.Course;
import com.zareal.ymcc.domain.CourseMarket;
import com.zareal.ymcc.domain.CourseOrder;
import com.zareal.ymcc.domain.CourseOrderItem;
import com.zareal.ymcc.dto.CourseOrderDto;
import com.zareal.ymcc.dto.PayDto;
import com.zareal.ymcc.dto.PayExtParamsDto;
import com.zareal.ymcc.enums.GlobalErrorCode;
import com.zareal.ymcc.feign.CourseServiceFeignClient;
import com.zareal.ymcc.mapper.CourseOrderMapper;
import com.zareal.ymcc.result.JSONResult;
import com.zareal.ymcc.service.ICourseOrderItemService;
import com.zareal.ymcc.service.ICourseOrderService;
import com.zareal.ymcc.util.AssertUtil;
import com.zareal.ymcc.util.CodeGenerateUtils;
import com.zareal.ymcc.vo.CourseInfoVo;
import com.zareal.ymcc.vo.CourseOrderVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author Wilson
 * @since 2022-10-19
 */
@Service
@Slf4j
public class CourseOrderServiceImpl extends ServiceImpl<CourseOrderMapper, CourseOrder> implements ICourseOrderService {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private CourseServiceFeignClient courseService;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    @Autowired
    private ICourseOrderItemService courseOrderItemService;
    /**
     * 下单判断，保存订单信息
     */
    @Override
    public String placeOrder(CourseOrderDto courseOrderDto) {
        List<Long> courseIds = courseOrderDto.getCourseIds();
        Integer payType = courseOrderDto.getPayType();
        String token = courseOrderDto.getToken();
        Integer type = courseOrderDto.getType();
        // 1.校验JSR303
        // 2.防重校验 下单成功删除redis中的订单信息，
        Long loginId = 3L; // @TODO 需要修改用户Id
        String idsStr = courseIds.stream().map(id -> String.valueOf(id)).collect(Collectors.joining(","));

        String orderTokenRedisKey = String.format(CourseOrderContants.redisOrderToken, loginId, idsStr);
        Object tokenTmp = redisTemplate.opsForValue().get(orderTokenRedisKey);
        // 断言是否存在 和传过来的是否相同
        AssertUtil.isNotNull(tokenTmp, GlobalErrorCode.ORDER_TOKEN_IS_NULL);
        Boolean boo = tokenTmp.toString().equals(token);
        AssertUtil.isTrue(boo,GlobalErrorCode.ORDER_TOKEN_IS_NULL);

        // 3.得到用户买课信息
        JSONResult courseInfo = courseService.getInfo(idsStr);
        AssertUtil.isTrue(courseInfo.isSuccess(), GlobalErrorCode.ORDER_COURSE_IS_NULL);
        Object courseInfoDataTmp = courseInfo.getData();
        AssertUtil.isNotNull(courseInfoDataTmp,GlobalErrorCode.ORDER_COURSE_IS_NULL);
        CourseInfoVo courseInfoVo = JSON.parseObject(JSON.toJSONString(courseInfoDataTmp), CourseInfoVo.class);
        // 4.保存主订单和订单快照
        List<CourseOrderVo> courseInfos = courseInfoVo.getCourseInfos();
        BigDecimal totalAmount = courseInfoVo.getTotalAmount();
        CourseOrder courseOrder = new CourseOrder();
        Date now = new Date();
        courseOrder.setCreateTime(now);
        courseOrder.setOrderNo(CodeGenerateUtils.generateOrderSn(loginId));
        courseOrder.setPayType(payType);
        courseOrder.setStatusOrder(CourseOrder.WAIT_PAY_STATUS);
        courseOrder.setTotalAmount(totalAmount);
        courseOrder.setTotalCount(courseInfos.size());
        courseOrder.setUserId(loginId);
        courseOrder.setVersion(0);
        // insert(courseOrder);
        // 购买课程 [java大神班;] , 支付[222.00] 元
        String title = CourseOrderContants.OrderTokenJoinParamOne;
        for (int i = 0; i < courseInfos.size(); i++) {
            Course course = courseInfos.get(i).getCourse();
            CourseMarket courseMarket = courseInfos.get(i).getCourseMarket();
            CourseOrderItem courseOrderItem = new CourseOrderItem();
            courseOrderItem.setAmount(BigDecimal.valueOf(courseMarket.getPrice()));
            courseOrderItem.setCount(1);
            courseOrderItem.setCourseId(course.getId());
            courseOrderItem.setCourseName(course.getName());
            courseOrderItem.setCoursePic(course.getPic());
            courseOrderItem.setCreateTime(now);
            //courseOrderItem.setOrderId(courseOrder.getId());
            courseOrderItem.setOrderNo(courseOrder.getOrderNo());
            courseOrderItem.setVersion(0);
            courseOrder.getCourseOrderItems().add(courseOrderItem);
            // courseOrderItemService.insert(courseOrderItem);
            title = title + course.getName() + ";";
        }
        title = title + CourseOrderContants.OrderTokenJoinParamTwo+totalAmount+CourseOrderContants.OrderTokenJoinParamThree;
        courseOrder.setTitle(title);
        // updateById(courseOrder);

        PayDto payDto = new PayDto();
        BeanUtils.copyProperties(courseOrder, payDto);


        // ==================扩展参数处理
        PayExtParamsDto payExtParamsDto = new PayExtParamsDto();
        payExtParamsDto.setCourseIds(idsStr);
        payExtParamsDto.setOrderNo(courseOrder.getOrderNo());
        payExtParamsDto.setUserId(loginId);
        payDto.setExtParams(JSON.toJSONString(payExtParamsDto)); // {"courseIds":"3,9","userId":"3","orderNo":"21312312"}

        // ==================扩展参数处理


        // 5.这里使用mq异步 创建一个
        Message<String> message = MessageBuilder.withPayload(JSON.toJSONString(payDto)).build();
        // 发送mq事务消息 object：是给监听器执行本地事务的，message是消费方进行消费的 1.订单编号、支付总金额、支付方式、订单标题、
        rocketMQTemplate.sendMessageInTransaction(
                "tx-course-order-group",
                "tx-course-order-topic:tx-course-order-tag",
                message,
                // 这个arg是传到监听器的参数
                JSON.toJSONString(courseOrder));
        // 删除redis防重token
        redisTemplate.delete(orderTokenRedisKey);

        try {
            // 发送延迟消息
            Message<String> delayMessage = MessageBuilder.withPayload(courseOrder.getOrderNo()).build();
            // String destination, Message<?> message, long timeout, int delayLevel
            SendResult sendResult = rocketMQTemplate.syncSend(
                    "course-order-handler-delay-topic:course-order-handler-delay-tag",
                    delayMessage,
                    2000,
                    3);// 测试设置30S，线上设置30分钟
            if (!sendResult.getSendStatus().equals(SendStatus.SEND_OK)){
                // 重试三次，如果还是不行，那么进行记录，给管理员......发消息,手动处理
                log.error("下单发送延迟消息失败，订单号为：{}", courseOrder.getOrderNo());
            }

        } catch (Exception e) {
            // 如果有异常，那么进行记录，给管理员......发消息
            log.error("下单发送延迟消息失败，订单号为：{}", courseOrder.getOrderNo());
            e.printStackTrace();
        }

        // 响应订单编号
        return courseOrder.getOrderNo();
    }

    @Override
    public CourseOrder selectByOrderNo(String orderNo) {
        Wrapper<CourseOrder> wrapper = new EntityWrapper<>();
        wrapper.eq("order_no", orderNo);
        return selectOne(wrapper);
    }


    @Override
    public void saveCourseOrderAndCourseOrderItems(CourseOrder courseOrder) {
        CourseOrder selectCourseOrder = selectByOrderNo(courseOrder.getOrderNo());
        if (selectCourseOrder == null){
            insert(courseOrder);
            courseOrder.getCourseOrderItems().forEach(e-> {
                e.setOrderId(courseOrder.getId());
                courseOrderItemService.insert(e);
            });
        }
    }
}
